package com.hzmg.web.creater.handler;

import cn.hutool.core.convert.Convert;
import com.google.common.collect.Lists;
import com.hzmg.web.common.ResultDTO;
import com.hzmg.web.common.WebConstant;
import com.hzmg.web.creater.factory.RequestFactory;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.Route;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;

import javax.annotation.Resource;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 路由handler通用模板方式
 * @author zbw
 */
@Slf4j
@Component
public abstract class SingleRouterHandler {

    @Resource
    private RequestFactory requestFactory;

    private SingleRouterHandler nextSingleRouterHandler;

    protected static void out(RoutingContext ctx, Object msg) {
        ctx.response()
                .putHeader(WebConstant.HTTP_HEADER_CONTENT_TYPE, WebConstant.JSON_MEDIA_TYPE)
                .end(JsonObject.mapFrom(msg).encode());
    }

    public SingleRouterHandler getNextSingleRouterHandler() {
        return nextSingleRouterHandler;
    }

    public void setNextSingleRouterHandler(SingleRouterHandler nextSingleRouterHandler) {
        this.nextSingleRouterHandler = nextSingleRouterHandler;
    }

    public void filter(Method method, String prefix, Object classBean, Router router) {
        doFilter(method, prefix, classBean, router);
        if (getNextSingleRouterHandler() != null) {
            getNextSingleRouterHandler().filter(method, prefix, classBean, router);
        }
    }

    public abstract void doFilter(Method method, String prefix, Object classBean, Router router);

    public void dealRouterTemplate(Method method, String prefix, Object classBean, Router router) {
        String url = getUrl(method);
        getRoute(router, prefix, url).handler(ctx -> webHandle(ctx, method, prefix, url, classBean))
                .failureHandler(frc -> {
                    log.warn("[vertx web]The API name:{} invoke failed,the reason is {}", prefix + url, frc.failure().toString());
                    out(frc, frc.toString());
                });

    }

    public abstract String getUrl(Method method);

    public abstract Route getRoute(Router router, String prefix, String url);

    protected void webHandle(RoutingContext ctx, Method method, String prefix, String url, Object classBean) {
        //该方法所有的参数
        List<Object> parameters = Lists.newArrayList();
        //遍历该方法的所有参数注解（注：可能有不带注解的参数）
        for (int i = 0; i < method.getParameterCount(); i++) {
            List<Class<? extends Annotation>> parameterAnnotations = getParameterAnnotations(method, i);
            requestFactory.dealRouter(parameterAnnotations, ctx, method, parameters, i);
        }

        Object[] objects = new Object[parameters.size()];
        for (int i = 0; i < parameters.size(); i++) {
            objects[i] = parameters.get(i);
            log.info("[vertx web] object:{}", objects[i]);
        }
        log.info("[vertx web] objects:{}", Arrays.toString(objects));
        log.info("[vertx web] api has benn invoke.The api name is:" + prefix + url);
        if ("void".equals(method.getReturnType().getTypeName())) {
            log.info("[vertx web] this method returnType is void");
            try {
                //method.invoke(classBean,parameters);
                ReflectionUtils.invokeMethod(method, classBean, objects);
            } catch (Exception e) {
                throw new RuntimeException(e.toString());
            }
            out(ctx, ResultDTO.success("ok"));
        } else {
            log.info("[vertx web] this method returnType isn't void");
            try {
                out(ctx, ReflectionUtils.invokeMethod(method, classBean, objects));

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private List<Class<? extends Annotation>> getParameterAnnotations(Method method, int i) {
        return Arrays
                .stream(method.getParameterAnnotations()[i])
                .map(Annotation::annotationType)
                .collect(Collectors.toList());
    }

    private Object typeConverter(String obj, Parameter parameter) {
        try {
            return Convert.convertQuietly(parameter.getType(), obj, null);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return obj;
    }

}
